#include "widget.h"
#include "graph.h"
#include "uinterface.h"

#include <QDebug>
#include <QMessageBox>
#include <QScreen>
#include <QComboBox>
#include <QCheckBox>
#include <QFontComboBox>

#include <string>
#include <cstdlib>
#include <windows.h>
#include <ctime>
using namespace std;

static inline void appendAndRoll(QTextBrowser* obj, const QString& s) {
    obj->append(s);
    obj->moveCursor(QTextCursor::End);
}

Widget::Widget(QWidget *parent)
    : QWidget(parent),
      wholeFrame(new QGridLayout),
      imageFrame(new QHBoxLayout),
      inputFrame(new QVBoxLayout),
      portFrame(new QHBoxLayout),
      btnFrame(new QHBoxLayout),
      terminalFrame(new QVBoxLayout),

      portInput(new QLineEdit),
      btnOpenPort(new QPushButton),
      btnClosePort(new QPushButton),
      portLabel(new QLabel),
      terminal(new QTextBrowser),
      btnSendData(new QPushButton),
      emmityInput(new QLineEdit),

      portHandle(HANDLE(-1)),
      manager(new ThreadManager),
      imageIr(new IMGWIDGET(manager))
{
#ifdef MY_DEBUG
    qDebug()<<"[enter]: Widget constructor";
#endif

    QGridLayout *wholewholeframe = new QGridLayout;
    QGridLayout *frame3d = new QGridLayout;
    /* set the ui */
    {
    portInput->setText("");

    btnOpenPort->setText(tr("Open Port"));

    btnClosePort->setText(tr("Close Port"));

#ifdef MY_DEBUG
    qDebug()<<imageIr->width()<<" "<<imageIr->height();
#endif
    portLabel->setText("PortName");

    terminal->clear();
    //terminal->setFixedSize(QSize(280, 70));
    terminal->append("Hello!Welcome to IRAI developed by our group.");
    appendAndRoll(terminal, "This box will show the information of your operations.");
//    terminalFrame->addWidget(emmityInput);
//    terminalFrame->addWidget(terminal);

    portFrame->addWidget(portLabel);
    portFrame->addWidget(portInput);

    btnFrame->addWidget(btnOpenPort);
    btnFrame->addWidget(btnClosePort);
    btnSendData->setText("SetEmmity");

    QGridLayout* emmityFrame = new QGridLayout;
    QLabel* emmityLable = new QLabel;
    emmityLable->setText("Emmity");
    emmityFrame->addWidget(emmityLable,0,0);
    emmityFrame->addWidget(emmityInput,0,1);

    terminalFrame->addLayout(emmityFrame);
    terminalFrame->addWidget(terminal);

    btnFrame->addWidget(btnSendData);
    inputFrame->addLayout(portFrame);
    inputFrame->addLayout(btnFrame);

    imageFrame->addWidget(imageIr);

    wholeFrame->addLayout(imageFrame,0,0);
    wholeFrame->addLayout(inputFrame,1,0);
    wholeFrame->addLayout(terminalFrame,2,0);

    connect(btnOpenPort, &QAbstractButton::clicked,this,&Widget::btnOpen_clicked,Qt::AutoConnection);
    connect(btnClosePort,&QAbstractButton::clicked,this,&Widget::btnClose_clicked,Qt::AutoConnection);
    connect(btnSendData,&QAbstractButton::clicked,this,&Widget::btnSend_clicked,Qt::AutoConnection);
    wholewholeframe->addLayout(wholeFrame,0,0);
    }

    {
        Q3DBars *widgetgraph = new Q3DBars();
        QWidget *container = QWidget::createWindowContainer(widgetgraph);
        //! [0]

        if (!widgetgraph->hasContext()) {
            QMessageBox msgBox;
            msgBox.setText("Couldn't initialize the OpenGL context.");
            msgBox.exec();
            assert(widgetgraph->hasContext());
        }

        QSize screenSize = widgetgraph->screen()->size();
        container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
        container->setMaximumSize(screenSize);
        container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        container->setFocusPolicy(Qt::StrongFocus);

        //! [1]
        QWidget *widget = new QWidget;
        QHBoxLayout *hLayout = new QHBoxLayout(widget);
        QVBoxLayout *vLayout = new QVBoxLayout();
        hLayout->addWidget(container, 1);
        hLayout->addLayout(vLayout);
        //! [1]

        widget->setWindowTitle(QStringLiteral("The temperature image of IR data, designed by Xiang Feng."));

        QComboBox *themeList = new QComboBox(widget);
        themeList->addItem(QStringLiteral("Qt"));
        themeList->addItem(QStringLiteral("Primary Colors"));
        themeList->addItem(QStringLiteral("Digia"));
        themeList->addItem(QStringLiteral("Stone Moss"));
        themeList->addItem(QStringLiteral("Army Blue"));
        themeList->addItem(QStringLiteral("Retro"));
        themeList->addItem(QStringLiteral("Ebony"));
        themeList->addItem(QStringLiteral("Isabelle"));
        themeList->setCurrentIndex(0);

        QPushButton *labelButton = new QPushButton(widget);
        labelButton->setText(QStringLiteral("Change label style"));

        QCheckBox *smoothCheckBox = new QCheckBox(widget);
        smoothCheckBox->setText(QStringLiteral("Smooth bars"));
        smoothCheckBox->setChecked(false);


        QComboBox *barStyleList = new QComboBox(widget);
        barStyleList->addItem(QStringLiteral("Bar"), int(QAbstract3DSeries::MeshBar));
        barStyleList->addItem(QStringLiteral("Pyramid"), int(QAbstract3DSeries::MeshPyramid));
        barStyleList->addItem(QStringLiteral("Cone"), int(QAbstract3DSeries::MeshCone));
        barStyleList->addItem(QStringLiteral("Cylinder"), int(QAbstract3DSeries::MeshCylinder));
        barStyleList->addItem(QStringLiteral("Bevel bar"), int(QAbstract3DSeries::MeshBevelBar));
        barStyleList->addItem(QStringLiteral("Sphere"), int(QAbstract3DSeries::MeshSphere));
        barStyleList->setCurrentIndex(4);

        QPushButton *cameraButton = new QPushButton(widget);
        cameraButton->setText(QStringLiteral("Change camera preset"));

        QPushButton *zoomToSelectedButton = new QPushButton(widget);
        zoomToSelectedButton->setText(QStringLiteral("Zoom to selected bar"));

        QPushButton *setDataButton = new QPushButton(widget);
        setDataButton->setText(QStringLiteral("Set Data"));

        QComboBox *selectionModeList = new QComboBox(widget);
        selectionModeList->addItem(QStringLiteral("None"),
                                   int(QAbstract3DGraph::SelectionNone));
        selectionModeList->addItem(QStringLiteral("Bar"),
                                   int(QAbstract3DGraph::SelectionItem));
        selectionModeList->addItem(QStringLiteral("Row"),
                                   int(QAbstract3DGraph::SelectionRow));
        selectionModeList->addItem(QStringLiteral("Bar and Row"),
                                   int(QAbstract3DGraph::SelectionItemAndRow));
        selectionModeList->addItem(QStringLiteral("Column"),
                                   int(QAbstract3DGraph::SelectionColumn));
        selectionModeList->addItem(QStringLiteral("Bar and Column"),
                                   int(QAbstract3DGraph::SelectionItemAndColumn));
        selectionModeList->addItem(QStringLiteral("Row and Column"),
                                   int(QAbstract3DGraph::SelectionRowAndColumn));
        selectionModeList->addItem(QStringLiteral("Bar, Row and Column"),
                                   int(QAbstract3DGraph::SelectionItemRowAndColumn));
        selectionModeList->addItem(QStringLiteral("Slice into Row"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionRow));
        selectionModeList->addItem(QStringLiteral("Slice into Row and Item"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow));
        selectionModeList->addItem(QStringLiteral("Slice into Column"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionColumn));
        selectionModeList->addItem(QStringLiteral("Slice into Column and Item"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn));
        selectionModeList->addItem(QStringLiteral("Multi: Bar, Row, Col"),
                                   int(QAbstract3DGraph::SelectionItemRowAndColumn
                                       | QAbstract3DGraph::SelectionMultiSeries));
        selectionModeList->addItem(QStringLiteral("Multi, Slice: Row, Item"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndRow
                                       | QAbstract3DGraph::SelectionMultiSeries));
        selectionModeList->addItem(QStringLiteral("Multi, Slice: Col, Item"),
                                   int(QAbstract3DGraph::SelectionSlice | QAbstract3DGraph::SelectionItemAndColumn
                                       | QAbstract3DGraph::SelectionMultiSeries));
        selectionModeList->setCurrentIndex(1);


        QCheckBox *backgroundCheckBox = new QCheckBox(widget);
        backgroundCheckBox->setText(QStringLiteral("Show background"));
        backgroundCheckBox->setChecked(false);

        QCheckBox *gridCheckBox = new QCheckBox(widget);
        gridCheckBox->setText(QStringLiteral("Show grid"));
        gridCheckBox->setChecked(true);
    #if 0
        QCheckBox *seriesCheckBox = new QCheckBox(widget);
        seriesCheckBox->setText(QStringLiteral("Show second series"));
        seriesCheckBox->setChecked(false);
    #endif
        QCheckBox *reverseValueAxisCheckBox = new QCheckBox(widget);
        reverseValueAxisCheckBox->setText(QStringLiteral("Reverse value axis"));
        reverseValueAxisCheckBox->setChecked(false);

        QCheckBox *reflectionCheckBox = new QCheckBox(widget);
        reflectionCheckBox->setText(QStringLiteral("Show reflections"));
        reflectionCheckBox->setChecked(false);

        //! [4]
        QSlider *rotationSliderX = new QSlider(Qt::Horizontal, widget);
        rotationSliderX->setTickInterval(30);
        rotationSliderX->setTickPosition(QSlider::TicksBelow);
        rotationSliderX->setMinimum(-180);
        rotationSliderX->setValue(0);
        rotationSliderX->setMaximum(180);
        QSlider *rotationSliderY = new QSlider(Qt::Horizontal, widget);
        rotationSliderY->setTickInterval(15);
        rotationSliderY->setTickPosition(QSlider::TicksAbove);
        rotationSliderY->setMinimum(-90);
        rotationSliderY->setValue(0);
        rotationSliderY->setMaximum(90);
        //! [4]

        QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, widget);
        fontSizeSlider->setTickInterval(10);
        fontSizeSlider->setTickPosition(QSlider::TicksBelow);
        fontSizeSlider->setMinimum(1);
        fontSizeSlider->setValue(30);
        fontSizeSlider->setMaximum(100);

        QFontComboBox *fontList = new QFontComboBox(widget);
        fontList->setCurrentFont(QFont("Times New Roman"));

        QComboBox *shadowQuality = new QComboBox(widget);
        shadowQuality->addItem(QStringLiteral("None"));
        shadowQuality->addItem(QStringLiteral("Low"));
        shadowQuality->addItem(QStringLiteral("Medium"));
        shadowQuality->addItem(QStringLiteral("High"));
        shadowQuality->addItem(QStringLiteral("Low Soft"));
        shadowQuality->addItem(QStringLiteral("Medium Soft"));
        shadowQuality->addItem(QStringLiteral("High Soft"));
        shadowQuality->setCurrentIndex(5);

        QComboBox *rangeList = new QComboBox(widget);
        for (int i=0,cnt=0; i<=3;i++) {
            bool flag=true;
            for (int j=0;flag&&j<10;j++) {
                if (cnt == 24) {
                    flag = false;
                    continue;
                }
                QString s;
                s.append("R_");s.append(i+'0');s.append(j+'0');
                rangeList->addItem(s);
                cnt++;
            }
            if (!flag) {
                rangeList->addItem(QStringLiteral("All"));
                rangeList->setCurrentIndex(8);
                break;
            }
        }

        QCheckBox *axisTitlesVisibleCB = new QCheckBox(widget);
        axisTitlesVisibleCB->setText(QStringLiteral("Axis titles visible"));
        axisTitlesVisibleCB->setChecked(true);

        QCheckBox *axisTitlesFixedCB = new QCheckBox(widget);
        axisTitlesFixedCB->setText(QStringLiteral("Axis titles fixed"));
        axisTitlesFixedCB->setChecked(true);

        QSlider *axisLabelRotationSlider = new QSlider(Qt::Horizontal, widget);
        axisLabelRotationSlider->setTickInterval(10);
        axisLabelRotationSlider->setTickPosition(QSlider::TicksBelow);
        axisLabelRotationSlider->setMinimum(0);
        axisLabelRotationSlider->setValue(30);
        axisLabelRotationSlider->setMaximum(90);

        //! [5]
        vLayout->addWidget(new QLabel(QStringLiteral("Rotate horizontally")));
        vLayout->addWidget(rotationSliderX, 0, Qt::AlignTop);
        vLayout->addWidget(new QLabel(QStringLiteral("Rotate vertically")));
        vLayout->addWidget(rotationSliderY, 0, Qt::AlignTop);
        //! [5]
        vLayout->addWidget(labelButton, 0, Qt::AlignTop);
        vLayout->addWidget(cameraButton, 0, Qt::AlignTop);
        vLayout->addWidget(zoomToSelectedButton, 0, Qt::AlignTop);
        vLayout->addWidget(setDataButton, 0, Qt::AlignTop);
        vLayout->addWidget(backgroundCheckBox);
        vLayout->addWidget(gridCheckBox);
        vLayout->addWidget(smoothCheckBox);
        vLayout->addWidget(reflectionCheckBox);
    #if 0
        vLayout->addWidget(seriesCheckBox);
    #endif
        vLayout->addWidget(reverseValueAxisCheckBox);
        vLayout->addWidget(axisTitlesVisibleCB);
        vLayout->addWidget(axisTitlesFixedCB);
        vLayout->addWidget(new QLabel(QStringLiteral("Show row")));
        vLayout->addWidget(rangeList);
        vLayout->addWidget(new QLabel(QStringLiteral("Change bar style")));
        vLayout->addWidget(barStyleList);
        vLayout->addWidget(new QLabel(QStringLiteral("Change selection mode")));
        vLayout->addWidget(selectionModeList);
        vLayout->addWidget(new QLabel(QStringLiteral("Change theme")));
        vLayout->addWidget(themeList);
        vLayout->addWidget(new QLabel(QStringLiteral("Adjust shadow quality")));
        vLayout->addWidget(shadowQuality);
        vLayout->addWidget(new QLabel(QStringLiteral("Change font")));
        vLayout->addWidget(fontList);
        vLayout->addWidget(new QLabel(QStringLiteral("Adjust font size")));
        vLayout->addWidget(fontSizeSlider);
        vLayout->addWidget(new QLabel(QStringLiteral("Axis label rotation")));
        vLayout->addWidget(axisLabelRotationSlider, 1, Qt::AlignTop);

        //! [2]
        MyGraph *modifier = new MyGraph(widgetgraph,this->manager);
        //! [2]

        //! [6]
        QObject::connect(rotationSliderX, &QSlider::valueChanged, modifier, &MyGraph::rotateX);
        QObject::connect(rotationSliderY, &QSlider::valueChanged, modifier, &MyGraph::rotateY);
        //! [6]

        QObject::connect(labelButton, &QPushButton::clicked, modifier,
                         &MyGraph::changeLabelBackground);
        QObject::connect(cameraButton, &QPushButton::clicked, modifier,
                         &MyGraph::changePresetCamera);
        QObject::connect(zoomToSelectedButton, &QPushButton::clicked, modifier,
                         &MyGraph::zoomToSelectedBar);
        QObject::connect(setDataButton, &QPushButton::clicked, modifier,
                         &MyGraph::setData);

        QObject::connect(backgroundCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setBackgroundEnabled);
        QObject::connect(gridCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setGridEnabled);
        QObject::connect(smoothCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setSmoothBars);
    #if 0
        QObject::connect(seriesCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setSeriesVisibility);
    #endif
        QObject::connect(reverseValueAxisCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setReverseValueAxis);
        QObject::connect(reflectionCheckBox, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setReflection);

        QObject::connect(modifier, &MyGraph::backgroundEnabledChanged,
                         backgroundCheckBox, &QCheckBox::setChecked);
        QObject::connect(modifier, &MyGraph::gridEnabledChanged,
                         gridCheckBox, &QCheckBox::setChecked);

        QObject::connect(rangeList, SIGNAL(currentIndexChanged(int)), modifier,
                         SLOT(changeRange(int)));

        QObject::connect(barStyleList, SIGNAL(currentIndexChanged(int)), modifier,
                         SLOT(changeStyle(int)));

        QObject::connect(selectionModeList, SIGNAL(currentIndexChanged(int)), modifier,
                         SLOT(changeSelectionMode(int)));

        QObject::connect(themeList, SIGNAL(currentIndexChanged(int)), modifier,
                         SLOT(changeTheme(int)));

        QObject::connect(shadowQuality, SIGNAL(currentIndexChanged(int)), modifier,
                         SLOT(changeShadowQuality(int)));

        QObject::connect(modifier, &MyGraph::shadowQualityChanged, shadowQuality,
                         &QComboBox::setCurrentIndex);
        QObject::connect(widgetgraph, &Q3DBars::shadowQualityChanged, modifier,
                         &MyGraph::shadowQualityUpdatedByVisual);

        QObject::connect(fontSizeSlider, &QSlider::valueChanged, modifier,
                         &MyGraph::changeFontSize);
        QObject::connect(fontList, &QFontComboBox::currentFontChanged, modifier,
                         &MyGraph::changeFont);

        QObject::connect(modifier, &MyGraph::fontSizeChanged, fontSizeSlider,
                         &QSlider::setValue);
        QObject::connect(modifier, &MyGraph::fontChanged, fontList,
                         &QFontComboBox::setCurrentFont);

        QObject::connect(axisTitlesVisibleCB, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setAxisTitleVisibility);
        QObject::connect(axisTitlesFixedCB, &QCheckBox::stateChanged, modifier,
                         &MyGraph::setAxisTitleFixed);
        QObject::connect(axisLabelRotationSlider, &QSlider::valueChanged, modifier,
                         &MyGraph::changeLabelRotation);
        //! [3]
        frame3d->addWidget(widget);
        wholewholeframe->addLayout(frame3d,0,1);
    }
    setLayout(wholewholeframe);    
}

Widget::~Widget()
{
#ifdef MY_DEBUG
    qDebug()<<"[enter]: Widget destructor"<<endl;
#endif
    delete imageIr;
    delete portInput;
    delete btnOpenPort;
    delete btnClosePort;
    delete portLabel;
    delete terminal;
    delete imageFrame;
    delete inputFrame;
    delete portFrame;
    delete btnFrame;
    delete wholeFrame;
}

void Widget::btnOpen_clicked() {
#if 0
    manager->imgwidget = imageIr;
    manager->startThread();
    return;
#endif

    if (portHandle!=HANDLE(-1)) {
        appendAndRoll(terminal,"\nCurrent port is running! Please close current port at first.");
        return;
    }
    QString getInput = portInput->text();
    string getStr = getInput.toStdString();
    wstring getWstr = uInterface::s2ws(getStr);
    LPCWSTR pWstr = getWstr.c_str();
#ifdef MY_DEBUG
    qDebug()<<getInput;
#endif

    portHandle = CreateFile(pWstr,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
    if (portHandle!=HANDLE(-1)) {
        appendAndRoll(terminal,"\nSuccessfully open the port!");

        /* set up port I/O buffer size */
        SetupComm(portHandle,port_param::inputBufferSize,port_param::outputBufferSize);

        /* set up port timeout parameter */
        COMMTIMEOUTS TimeOuts;
        TimeOuts.ReadIntervalTimeout = 100;
        TimeOuts.ReadTotalTimeoutMultiplier = 500;
        TimeOuts.ReadTotalTimeoutConstant = 5000;
        TimeOuts.WriteTotalTimeoutMultiplier = 500;
        TimeOuts.WriteTotalTimeoutConstant = 2000;
        SetCommTimeouts(portHandle, &TimeOuts);

        /* set up transform mode RX/TX */
        DCB dcb1;
        GetCommState(portHandle, &dcb1);
        dcb1.BaudRate = 115200;
        dcb1.ByteSize = 8;
        dcb1.Parity = NOPARITY;
        dcb1.StopBits = TWOSTOPBITS;
        dcb1.fParity = FALSE;
        dcb1.fNull = FALSE;
        SetCommState(portHandle, &dcb1);

        /* clear port I/O buffer */
        PurgeComm(portHandle, PURGE_TXCLEAR | PURGE_RXCLEAR);

        /* read the useless data head */
        BYTE openlabel[3] = { 0 };
#ifdef MY_DEBUG
        qDebug()<<"begin read useless data frame head";
#endif
        while (true)
        {
#ifdef MY_DEBUG
            qDebug()<<portHandle;
#endif
            uInterface::ReadXBytesFromPort(portHandle,openlabel,1);
#ifdef MY_DEBUG
            qDebug()<<openlabel[0];
#endif
            if (*openlabel == 0x5A)
            {
                uInterface::ReadXBytesFromPort(portHandle,openlabel,3);
                if (openlabel[0] == 0x5A && openlabel[1] == 0x02 && openlabel[2] == 0x06){
                    for(int i=0;i<1540;i++) uInterface::ReadXBytesFromPort(portHandle,openlabel,1);
                    break;
                }
            }
        }
#ifdef MY_DEBUG
        qDebug()<<"while (true) break out";
#endif
        manager->port = portHandle;
        manager->imgwidget = imageIr;
        manager->startThread();
    } else {
        appendAndRoll(terminal,"\nOpen port failed! Please check if the port name is correct.");
    }
}

void Widget::btnClose_clicked() {
    qDebug()<<"btnclose_clicked";
#if 0
    manager->stopThread();
    return;
#endif

    if(portHandle==HANDLE(-1)) {
        appendAndRoll(terminal,"\nNo port is running! Please open a port at first.");
        return;
    }
    qDebug()<<"next: stopThread()";
    manager->stopThread();
    qDebug()<<"out: stopThread()";
    CloseHandle(portHandle);
    portHandle = HANDLE(-1);
    appendAndRoll(terminal,"\nSuccessfully close the port.");
}

void Widget::btnSend_clicked() {
    qDebug()<<"btnSend_clicked";
    QString str = this->emmityInput->text();
    double e = str.toDouble();
    if (e<0||e>1){
        appendAndRoll(terminal,"\nPlease input a number which is between [0, 1]!");
    } else {
        this->manager->emmity=e;
        this->manager->writeDone=false;
        appendAndRoll(terminal,"\nBegin setting emmity");
    }
}
